1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.hipaudio.audio; 12 13 public import hip.hipaudio.audioclip; 14 public import hip.hipaudio.audiosource; 15 public import hip.api.audio; 16 import hip.hipaudio.config; 17 18 //Backends 19 20 static if(HasOpenAL){import hip.hipaudio.backend.openal.player;} 21 static if(HasOpenSLES){import hip.hipaudio.backend.opensles.player;} 22 static if(HasXAudio2){import hip.hipaudio.backend.xaudio.player;} 23 static if(HasAVAudioEngine){import hip.hipaudio.backend.avaudio.player;} 24 import hip.hipaudio.backend.nullaudio; 25 26 27 import hip.audio_decoding.audio; 28 import hip.math.utils:getClosestMultiple; 29 import hip.util.reflection; 30 import hip.error.handler; 31 import hip.hipaudio.backend.webaudio.player; 32 33 version(Standalone) 34 { 35 alias HipAudioSourceAPI = HipAudioSource; 36 alias HipAudioClipAPI = HipAudioClip; 37 } 38 else 39 { 40 alias HipAudioSourceAPI = AHipAudioSource; 41 alias HipAudioClipAPI = IHipAudioClip; 42 } 43 44 /** 45 * This is an interface that should be created only once inside the application. 46 * Every audio function is global, meaning that every AudioSource will refer to the player 47 */ 48 public interface IHipAudioPlayer 49 { 50 //LOAD RELATED 51 public bool play_streamed(AHipAudioSource src); 52 public IHipAudioClip getClip(); 53 public IHipAudioClip loadStreamed(string path, uint chunkSize); 54 public void updateStream(AHipAudioSource source); 55 public AHipAudioSource getSource(bool isStreamed); 56 57 public void onDestroy(); 58 public void update(); 59 } 60 61 class HipAudio 62 { 63 public static bool initialize(HipAudioImplementation implementation = HipAudioImplementation.OpenAL, 64 bool hasProAudio = false, 65 bool hasLowLatencyAudio = false, 66 int optimalBufferSize = 4096, 67 int optimalSampleRate = 44_100) 68 { 69 ErrorHandler.startListeningForErrors("HipremeAudio initialization"); 70 _hasInitializedAudio = true; 71 HipAudio.is3D = is3D; 72 audioInterface = getAudioInterface(implementation); 73 HipAudio.hasProAudio = hasProAudio; 74 HipAudio.hasLowLatencyAudio = hasLowLatencyAudio; 75 HipAudio.optimalBufferSize = optimalBufferSize; 76 HipAudio.optimalSampleRate = optimalSampleRate; 77 return ErrorHandler.stopListeningForErrors(); 78 } 79 @ExportD static bool pause(AHipAudioSource src) 80 { 81 src.isPlaying = false; 82 return false; 83 } 84 @ExportD static bool play_streamed(AHipAudioSource src) 85 { 86 audioInterface.play_streamed(src); 87 src.isPlaying = true; 88 return false; 89 } 90 @ExportD static IHipAudioClip getClip(){return audioInterface.getClip();} 91 92 /** 93 * Loads a file from disk, sets the chunkSize for streaming and does one decoding frame 94 */ 95 @ExportD static IHipAudioClip loadStreamed(string path, uint chunkSize = ushort.max+1) 96 { 97 chunkSize = getClosestMultiple(optimalBufferSize, chunkSize); 98 HipAudioClip buf = cast(HipAudioClip)audioInterface.loadStreamed(path, chunkSize); 99 return buf; 100 } 101 102 @ExportD static void updateStream(HipAudioSource source) 103 { 104 audioInterface.updateStream(source); 105 } 106 @ExportD static AHipAudioSource getSource(bool isStreamed = false, IHipAudioClip clip = null) 107 { 108 if(isStreamed) ErrorHandler.assertExit(clip !is null, "Can't get streamed source without any buffer"); 109 HipAudioSource ret = cast(HipAudioSource)audioInterface.getSource(isStreamed); 110 if(clip) 111 ret.clip = clip; 112 return ret; 113 } 114 @ExportD static void onDestroy() 115 { 116 if(audioInterface !is null) 117 audioInterface.onDestroy(); 118 audioInterface = null; 119 } 120 121 static void update() 122 { 123 if(audioInterface !is null) 124 audioInterface.update(); 125 } 126 127 private static IHipAudioPlayer getAudioInterface(HipAudioImplementation impl, 128 bool hasProAudio = false, 129 bool hasLowLatencyAudio = false, 130 int optimalBufferSize = 4096, 131 int optimalSampleRate = 44_100) 132 { 133 import hip.console.log; 134 final switch(impl) 135 { 136 case HipAudioImplementation.WebAudio: 137 { 138 version(WebAssembly) 139 { 140 return new HipWebAudioPlayer(AudioConfig.musicConfig); 141 } 142 else 143 { 144 loglnWarn("Tried to use WebAudio implementation, but not in WebAssembly. No audio available"); 145 goto case HipAudioImplementation.Null; 146 } 147 } 148 case HipAudioImplementation.OpenSLES: 149 static if(HasOpenSLES) 150 { 151 return new HipOpenSLESAudioPlayer(AudioConfig.androidConfig, 152 hasProAudio, 153 hasLowLatencyAudio, 154 optimalBufferSize, 155 optimalSampleRate); 156 break; 157 } 158 case HipAudioImplementation.XAudio2: 159 static if(HasXAudio2) 160 { 161 loglnInfo("Initializing XAudio2 with audio config ", AudioConfig.musicConfig); 162 return new HipXAudioPlayer(AudioConfig.musicConfig); 163 } 164 else 165 { 166 loglnWarn("Tried to use XAudio2 implementation, but no XAudio2 version was provided. OpenAL will be used instead"); 167 goto case HipAudioImplementation.OpenAL; 168 } 169 case HipAudioImplementation.AVAudioEngine: 170 { 171 static if(HasAVAudioEngine) 172 return new HipAVAudioPlayer(AudioConfig.androidConfig); 173 else 174 { 175 loglnWarn("Tried to use AVAudioEngine implementation, but no AVAudioEngine found. OpenAL will be used instead"); 176 goto case HipAudioImplementation.OpenAL; 177 } 178 } 179 case HipAudioImplementation.OpenAL: 180 { 181 static if(HasOpenAL) 182 { 183 //Please note that OpenAL HRTF(spatial sound) only works with Mono Channel 184 return new HipOpenALAudioPlayer(AudioConfig.musicConfig); 185 } 186 else 187 { 188 loglnWarn("Tried to use OpenAL implementation, but no OpenAL version was provided. No audio available."); 189 goto case HipAudioImplementation.Null; 190 } 191 } 192 case HipAudioImplementation.Null: 193 { 194 loglnWarn("No AudioInterface was found. Using NullAudio"); 195 return new HipNullAudio(); 196 } 197 } 198 } 199 200 201 202 protected __gshared bool hasProAudio; 203 protected __gshared bool hasLowLatencyAudio; 204 protected __gshared int optimalBufferSize; 205 protected __gshared int optimalSampleRate; 206 private __gshared bool is3D; 207 private __gshared uint activeSources; 208 209 __gshared IHipAudioPlayer audioInterface; 210 211 //Debug vars 212 private __gshared bool _hasInitializedAudio = false; 213 public bool hasInitializedAudio() => _hasInitializedAudio; 214 }